package com.elitesland.system.service.impl;

import com.elitesland.core.base.ApiCode;
import com.elitesland.core.base.BaseModel;
import com.elitesland.core.base.PagingVO;
import com.elitesland.core.exception.BusinessException;
import com.elitesland.security.service.entity.JwtUserDto;
import com.elitesland.system.convert.SysPermissionConvert;
import com.elitesland.system.convert.SysUserConvert;
import com.elitesland.system.entity.*;
import com.elitesland.system.param.SysPasswordUpdate;
import com.elitesland.system.param.SysUserNewParam;
import com.elitesland.system.param.SysUserQueryParam;
import com.elitesland.system.param.SysUserUpdateParam;
import com.elitesland.system.repo.SysUserRepo;
import com.elitesland.system.repo.SysUserRepoProc;
import com.elitesland.system.service.*;
import com.elitesland.system.vo.SysPermissionVO;
import com.elitesland.system.vo.SysRoleVO;
import com.elitesland.system.vo.SysUserVO;
import com.querydsl.core.types.Predicate;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <pre>
 * [功能说明]
 * </pre>
 *
 * @author Mir
 * @date 2020/6/16
 */
@Service
public class SysUserServiceImpl implements SysUserService {

    private SysUserRepo sysUserRepo;

    private SysUserRepoProc sysUserRepoProc;

    private PasswordEncoder passwordEncoder;

    private SysRoleService sysRoleService;

    private SysPermissionService sysPermissionService;

    private SysUserRoleService sysUserRoleService;

    private SysRolePermissionService sysRolePermissionService;

    @Autowired
    @Lazy
    public void setSysUserRepo(SysUserRepo sysUserRepo) {
        this.sysUserRepo = sysUserRepo;
    }

    @Autowired
    @Lazy
    public void setSysUserRepoProc(SysUserRepoProc sysUserRepoProc) {
        this.sysUserRepoProc = sysUserRepoProc;
    }

    @Autowired
    @Lazy
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Autowired
    public void setSysRoleService(SysRoleService sysRoleService) {
        this.sysRoleService = sysRoleService;
    }

    @Autowired
    public void setSysPermissionService(SysPermissionService sysPermissionService) {
        this.sysPermissionService = sysPermissionService;
    }

    @Autowired
    public void setSysUserRoleService(SysUserRoleService sysUserRoleService) {
        this.sysUserRoleService = sysUserRoleService;
    }

    @Autowired
    public void setSysRolePermissionService(SysRolePermissionService sysRolePermissionService) {
        this.sysRolePermissionService = sysRolePermissionService;
    }

    @Override
    public PagingVO<SysUserVO> search(SysUserQueryParam param) {

        val users = sysUserRepo.findAll(sysUserRepoProc.where(param), param.getPageRequest());

        val userIds = users.map(BaseModel::getId).stream().collect(Collectors.toList());
        val userRoles = sysUserRoleService.listRolesByUserIds(userIds);

        val userVos = users.getContent().stream().map(u -> {
            SysUserVO userVo = SysUserConvert.INSTANCE.doToVo(u);
            if (userRoles.containsKey(u.getId())) {
                userVo.setRoleNames(userRoles.get(u.getId())
                        .stream().map(SysRoleVO::getName).collect(Collectors.joining(",")));
            }
            return userVo;
        }).collect(Collectors.toList());

        return PagingVO.<SysUserVO>builder()
                .total(users.getTotalElements())
                .records(userVos)
                .build();
    }

    @Override
    public Optional<SysUserDTO> getById(Long id) {
        return sysUserRepo.findById(id).map(u -> {
            val userDto = SysUserConvert.INSTANCE.doToDto(u);

            val roles = sysUserRoleService.listRolesByUserId(u.getId());
            val roleIds = roles.stream().map(SysRoleVO::getId).collect(Collectors.toList());
            val permissions = sysRolePermissionService.listPermissionsByRoleIds(roleIds);
            userDto.setRoles(roles)
                    .setPermissions(permissions);

            return userDto;
        });
    }

    @Override
    public List<SysUserDO> getByIds(List<Long> ids) {
        return sysUserRepo.findAllByIdIn(ids);
    }

    @Override
    public Optional<SysUserDTO> getUserByUsernameSec(String username) {
        return sysUserRepo.findByUsername(username).map(u -> {
            val user = SysUserConvert.INSTANCE.doToDto(u);

            user.setRoles(sysUserRoleService.listRolesByUserId(user.getId()));

            val roleIds = user.getRoles().stream().map(SysRoleVO::getId).collect(Collectors.toList());
            user.setPermissions(sysRolePermissionService.listPermissionsByRoleIds(roleIds));

            return user;
        });
    }

    @Override
    public Optional<SysUserVO> getUserByUsernameContaining(String username) {
        return sysUserRepo.findByUsernameContaining(username).map(SysUserConvert.INSTANCE::doToVo);
    }

    @Override
    public Optional<SysUserVO> getUserByUsername(String username) {
        val userOpt = getUserByUsernameSec(username);

        return userOpt.map(SysUserConvert.INSTANCE::doToVo);
    }

    @Override
    public boolean isUserExists(String username) {
        val userOpt = getUserByUsernameSec(username);
        return userOpt.isPresent();
    }

    @Override
    public Optional<UserDetails> loadUserDetailsByUsername(String username) {
        val userOpt = getUserByUsernameSec(username);
        return userOpt.map(o -> User.builder()
                .username(o.getUsername())
                .password(o.getPassword())
                .authorities(AuthorityUtils.createAuthorityList(
                        o.getRoles().stream().map(SysRoleVO::getCode).toArray(String[]::new))
                )
                .build());
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Long create(SysUserNewParam user) throws BusinessException {
        val oldUserOpt = getUserByUsernameSec(user.getUsername());
        if (oldUserOpt.isPresent()) {
            throw new BusinessException("用户：" + user.getUsername() + ", 已经存在");
        }

        if (StringUtils.isNotBlank(user.getMobile())) {
            val mobileOpt = sysUserRepo.findByMobile(user.getMobile());
            if (mobileOpt.isPresent()) {
                throw new BusinessException("相同的手机号码已经存在");
            }
        }

        if (StringUtils.isNotBlank(user.getEmail())) {
            val emailOpt = sysUserRepo.findByEmail(user.getEmail());
            if (emailOpt.isPresent()) {
                throw new BusinessException("相同的邮箱地址已经存在");
            }
        }

        val sysUser = SysUserConvert.INSTANCE.newToDo(user);
        sysUser.setPassword(passwordEncoder.encode(user.getPassword()));

        val newUser = sysUserRepo.save(sysUser);

        if (user.getRoleIds() != null) {
            user.getRoleIds().ifPresent(roleIds -> {
                val userRoles = roleIds.stream().map(roleId -> new SysUserRoleDO()
                        .setUserId(newUser.getId())
                        .setRoleId(roleId)
                ).collect(Collectors.toList());
                sysUserRoleService.saveAll(userRoles);
            });
        }
        return newUser.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SysUserUpdateParam user) {
        val userOpt = sysUserRepo.findById(user.getId());
        if (userOpt.isEmpty()) {
            throw new BusinessException("ID为：" + user.getId() + "， 的用户不存在");
        }
        val u = userOpt.get();

        if (StringUtils.isNotBlank(user.getFirstName())) {
            u.setFirstName(user.getFirstName());
        }
        if (StringUtils.isNotBlank(user.getLastName())) {
            u.setLastName(user.getLastName());
        }
        u.setEnabled(user.getEnabled());

        val sysUser = QSysUserDO.sysUserDO;
        if (StringUtils.isNotBlank(user.getMobile())) {
            val mobileCount = sysUserRepo.count(sysUser.mobile.eq(user.getMobile()).and(sysUser.id.ne(user.getId())));
            if (mobileCount > 0) {
                throw new BusinessException("手机号码：" + user.getMobile() + ", 已经存在");
            }
            u.setMobile(user.getMobile());
        }
        if (StringUtils.isNotBlank(user.getEmail())) {
            val emailCount = sysUserRepo.count(sysUser.email.eq(user.getEmail()).and(sysUser.id.ne(user.getId())));
            if (emailCount > 0) {
                throw new BusinessException("电子邮箱：" + user.getEmail() + ", 已经存在");
            }
            u.setEmail(user.getEmail());
        }

        if (StringUtils.isNotBlank(user.getPassword())) {
            u.setPassword(passwordEncoder.encode(user.getPassword()));
        }
        /*
         * 修改用户信息信息时，未删除用户角色关联表信息，报联合主键已存在的错误   shihao.ma  2020-12-10
         */
        sysUserRoleService.removeRolesByUserIds(Collections.singletonList(u.getId()));

        val userRoles = user.getRoleIds().stream().map(rid -> new SysUserRoleDO()
                .setUserId(u.getId())
                .setRoleId(rid)).collect(Collectors.toList());
        sysUserRoleService.saveAll(userRoles);

        sysUserRepo.save(u);


    }

    @Override
    public void changePasswordNoSec(Long userId, String newPassword) {
        sysUserRepo.findById(userId).ifPresent(u -> {
            u.setPassword(passwordEncoder.encode(newPassword));
            sysUserRepo.save(u);
        });
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Boolean switchUserStatus(Long id) {
        val userOpt = sysUserRepo.findById(id);
        if (userOpt.isEmpty()) {
            throw new BusinessException(ApiCode.FAIL, "ID为：" + id + "，的用户不存在");
        }
        val user = userOpt.get();
        user.setEnabled(!user.getEnabled());
        sysUserRepo.save(user);
        return user.getEnabled();
    }

    @Override
    public Set<SysRoleVO> listRolesByUsername(String username) {
        return getUserByUsername(username).map(u -> {
            return sysUserRoleService.listRolesByUserId(u.getId());
        }).orElseGet(HashSet::new);
    }

    @Override
    public Optional<JwtUserDto> current() {
        val principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof JwtUserDto) {
            val jwtUserDto = (JwtUserDto) principal;
            val user = jwtUserDto.getUser();
            user.setPassword("");
            return Optional.of(jwtUserDto);
        }
        return Optional.empty();
    }

    @Override
    public List<SysPermissionVO> currentMenu() {
        return current().map(u -> {
            val permSet = u.getUser().getPermissions().values().stream().flatMap(Collection::stream)
                    .filter(p -> p.getPermType() == 0)
                    .collect(Collectors.toSet());
            ;

            val idSet = permSet.stream().map(SysPermissionVO::getId).collect(Collectors.toSet());
            val pidSet = permSet.stream().flatMap(p -> Arrays.stream(p.getPath().split("/")).filter(StringUtils::isNotBlank))
                    .map(Long::parseLong).collect(Collectors.toSet());
            idSet.addAll(pidSet);

            val permissions = sysPermissionService.listAllPermissionsByIds(new ArrayList<>(idSet));

            val sortPerms = new ArrayList<SysPermissionDO>();

            //一级菜单
            permissions.stream().filter(p -> p.getParentId() == 0L)
                    .sorted(Comparator.comparing(SysPermissionDO::getSortNo)).forEach(tp -> {
                tp.setPermLevel(1);
                sortPerms.add(tp);

                AtomicInteger depth = new AtomicInteger(1);
                //二级菜单
                permissions.stream().filter(p -> p.getParentId().equals(tp.getId()))
                        .sorted(Comparator.comparing(SysPermissionDO::getSortNo)).forEach(sp -> {
                    if (depth.get() < 2) {
                        depth.set(2);
                    }
                    sp.setPermLevel(2);
                    sortPerms.add(sp);

                    //三级菜单
                    permissions.stream().filter(p -> p.getParentId().equals(sp.getId()))
                            .sorted(Comparator.comparing(SysPermissionDO::getSortNo)).forEach(tdp -> {
                        if (depth.get() < 3) {
                            depth.set(3);
                        }
                        tdp.setPermLevel(3);
                        sortPerms.add(tdp);

                        //四级菜单
                        val level4Menus = permissions.stream().filter(p -> p.getParentId().equals(tdp.getId()))
                                .sorted(Comparator.comparing(SysPermissionDO::getSortNo))
                                .collect(Collectors.toList());
                        if (!level4Menus.isEmpty()) {
                            if (depth.get() < 4) {
                                depth.set(4);
                            }
                            sortPerms.addAll(level4Menus);
                        }
                    });
                });

                tp.setMenuDepth(depth.get());
            });

            return sortPerms.stream().map(SysPermissionConvert.INSTANCE::doToVO).collect(Collectors.toList());
        }).orElseGet(ArrayList::new);
    }

    @Override
    public Set<SysPermissionVO> currentActions() {
        return current().map(u -> {
            return u.getUser().getPermissions().values().stream().flatMap(Collection::stream)
                    .filter(p -> p.getPermType() == 1)
                    .collect(Collectors.toSet());
        }).orElseGet(HashSet::new);
    }

    @Override
    public Set<SysPermissionVO> currentMenuActions(Long menuId) {
        return current().map(u -> {
            return u.getUser().getPermissions().values().stream().flatMap(Collection::stream)
                    .filter(p -> p.getParentId().equals(menuId))
                    .collect(Collectors.toSet());
        }).orElseGet(HashSet::new);
    }

    @Override
    public PagingVO<SysUserVO> listUsersBySourceType(String sourceType, PageRequest pageRequest) {
        val userTable = QSysUserDO.sysUserDO;

        Predicate predicate = userTable.sourceType.eq(sourceType);
        val ret = sysUserRepo.findAll(predicate, pageRequest);

        return PagingVO.<SysUserVO>builder()
                .total(ret.getTotalElements())
                .records(ret.getContent().stream().map(SysUserConvert.INSTANCE::doToVo).collect(Collectors.toList()))
                .build();
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void enableById(Long id) {
        sysUserRepo.findById(id).ifPresent(u -> {
            u.setEnabled(true);

            sysUserRepo.save(u);
        });
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void disableById(Long id) {
        sysUserRepo.findById(id).ifPresent(u -> {
            u.setEnabled(false);

            sysUserRepo.save(u);
        });
    }

    @Override
    public Optional<SysUserVO> findByMobile(String mobile) {
        Optional<SysUserDO> o = sysUserRepo.findByMobile(mobile);
        return o.map(SysUserConvert.INSTANCE::doToVo);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void removeByBatch(List<Long> userIds) {
        sysUserRepo.deleteAllByIdIn(userIds);
    }

    @Override
    public List<SysUserDO> getUnTransUser() {
        return sysUserRepo.findAllByTransFlagAndDeleteFlag(0, 0);
    }

    @Override
    public Integer setTransFlag(List<Long> ids, Integer transFlag) {
        return sysUserRepo.updateTransFlag(ids, transFlag);
    }

    @Override
    @Transactional
    public void deleteBatch(List<Long> list) {
        list.forEach(sysUserRepo::deleteById);
    }

    @Override
    public List<SysUserDO> getUnTransUser(List<Long> susUsrIDs) {
        return sysUserRepo.findAllByDeleteFlagAndIdIn(0, susUsrIDs);
    }

    @Override
    public void updatePassword(SysPasswordUpdate update) {
        sysUserRepo.findById(update.getUserId()).ifPresent(u -> {
            if (passwordEncoder.matches(update.getCurrentPassword(), u.getPassword())) {
                u.setPassword(passwordEncoder.encode(update.getNewPassword()));
                sysUserRepo.save(u);
            } else {
                throw new BusinessException("原始密码不正确");
            }
        });
    }
}
